home *** CD-ROM | disk | FTP | other *** search
/ The Best of MacTutor - S…e Code for Volumes 1 to 5 / The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin / Source Code / #30 (Mar 88) / chroma source.mpw / Chroma.p < prev    next >
Text File  |  1988-02-09  |  28KB  |  828 lines

  1. {
  2.  File Chroma.p
  3.  
  4.  This is a DA to show the current color assignments of all
  5.  connected displays, without taking up the whole screen
  6. (unless you want to zoom the window!).
  7.  
  8.  By Greg Marriott
  9.  Special thanks to Scott T. Boyd, Chris Derossi, & Darin Adler
  10.  
  11.  Copyright 1988, The MacHax™ Group.
  12.  All rights reserved.
  13.  
  14. 1.0d1  1-23-88    Created DA that puts up a window, and
  15.                   plots colors of MainDevice
  16. 1.0d2  1-24-88    Made window wider, added slot number of
  17.                   MainDevice
  18.        1-24-88    Beefed up error detection(but not
  19.                   necessarily correction %-)
  20. 1.0d3  1-25-88    Added brag pictures, b/w pictures
  21.        1-25-88    Call SysEnvirons to detect colorQD
  22.        1-25-88    Added word 'Slot' above slot number
  23. 1.0d4  1-26-88    Miscellaneous bug/cosmetic fixes
  24. 1.0d5  1-28-88    Made updates smarter. Now it clips to
  25.                   each device and draws with their
  26.                   characteristics(i.e. depth and color
  27.                   table)
  28.        1-28-88    Added zooming, then fixed zooming
  29.        1-29-88    Revised zooming to fill device of
  30.                   interest, instead of main device
  31. 1.0b1  1-29-88    Revved to beta and selected testers
  32. 1.0b2  2- 4-88    Scott made zooming work right, allowing
  33.                   the use of FindWindow.
  34. 1.0b3  2- 7-88    Began to use Palette Manager, as Chris
  35.                   suggested.
  36.        2- 8-88    Added more slot info to window, including
  37.                   gdFlags, gdRefNum, and gdMode, all hex
  38.                   numbers.
  39.        2- 8-88    Revised zooming, added option-zoom feature
  40. 1.0    2- 9-88    Published!
  41. }
  42.  
  43. {$D+} { Debugging symbols on }
  44.  
  45. UNIT Chroma;
  46.  
  47. INTERFACE
  48.  
  49. USES
  50.   MemTypes, QuickDraw, OSIntf, ToolIntf, PaletteMgr, PackIntf;
  51.  
  52.  
  53. FUNCTION DRVROpen(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  54. FUNCTION DRVRControl(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  55. FUNCTION DRVRStatus(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  56. FUNCTION DRVRPrime(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  57. FUNCTION DRVRClose(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  58.       
  59.  
  60. IMPLEMENTATION
  61.  
  62. CONST
  63.   versID = '1.0';
  64.   windowVSize = 96;
  65.  
  66.   bwChromaPicture = 0;
  67.   colorChromaPicture = 1;
  68.   sorryPicture = 2;
  69.  
  70.   infoStrings = 0;
  71.     firstInfoStr = 1;
  72.     slotStr = 1;
  73.     refNumStr = 2;
  74.     flagsStr = 3;
  75.     modeStr = 4;
  76.     lastInfoStr = 4;
  77.  
  78. TYPE
  79.   ChromaStorage = RECORD
  80.     theDevice:GDHandle;
  81.     minSize:Point;
  82.     windowPlace:Rect;
  83.     hasColorQD:Boolean;
  84.   END;
  85.   ChromaStoragePtr = ^ChromaStorage;
  86.   ChromaStorageHandle = ^ChromaStoragePtr;
  87.   
  88.   EventPtr = ^EventRecord;
  89.   trix =  RECORD      { needed for some coercive behavior }
  90.     CASE Boolean of
  91.       TRUE : (dontCare:ARRAY[0..10] OF Integer);
  92.       FALSE : (theEventPtr:  EventPtr);
  93.     END;
  94.  
  95.   WStatePtr = ^WStateData;
  96.   WStateHandle = ^WStatePtr;
  97.  
  98. FUNCTION RsrcID(dCtl:DCtlPtr;localID:INTEGER):Integer;
  99. BEGIN
  100.     { Calculate resource ID's based on our refNum }
  101.   RsrcID := (BOR($C000,(BSL(BNOT(dCtl^.dCtlRefNum),5))))
  102.               + localID;
  103. END;
  104.  
  105. FUNCTION DRVROpen(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  106.   
  107.   FUNCTION MinWindowSize:Point;
  108.   VAR
  109.     theStringList:Handle;
  110.     theString:Str255;
  111.     widest:INTEGER;
  112.     i:INTEGER;
  113.   BEGIN
  114.       { The vertical minimum is predetermined }
  115.     MinWindowSize.v := windowVSize;
  116.     widest := 0;
  117.     
  118.       { Read all the strings, finding the widest one }
  119.     FOR i := firstInfoStr TO lastInfoStr DO
  120.     BEGIN
  121.       GetIndString(theString, RsrcID(dCtl, infoStrings), i);
  122.       IF StringWidth(theString) > widest THEN
  123.         widest := StringWidth(theString);
  124.     END;
  125.     
  126.       { Add a little margin }
  127.     widest := widest + 2;
  128.     MinWindowSize.h := windowVSize + widest;
  129.   END; { FUNCTION MinWindowSize }
  130.  
  131. VAR   
  132.   oldPort:GrafPtr;
  133.   wFrame:Rect;
  134.   myWindow:WindowPtr;
  135.   myWindowSize:Point;
  136.   theEnv:SysEnvRec;
  137.   envError:OSErr;
  138.   aPalette:PaletteHandle;
  139.  
  140. BEGIN
  141.     { If there's no window yet, put one up... }
  142.   IF dCtl^.dCtlWindow = NIL THEN
  143.   BEGIN
  144.       { Pick a size for the window, even though we're
  145.         gonna change that before the window becomes visible }
  146.     SetRect(wFrame, 0, 0, windowVSize, windowVSize);
  147.     OffSetRect(wFrame, 100, 100);
  148.     
  149.       { Ask SysEnvirons if we have colorQD.  If not, put up
  150.         a "regular" window instead of a color window }
  151.     envError := SysEnvirons(1, theEnv);
  152.     IF theEnv.hasColorQD THEN
  153.         myWindow := NewCWindow(NIL, wFrame, 'Chroma', FALSE,
  154.                       zoomNoGrow, WindowPtr(-1), TRUE,
  155.                       LongInt(0))
  156.     ELSE
  157.         myWindow := NewWindow(NIL, wFrame, 'Chroma', FALSE,
  158.                       noGrowDocProc, WindowPtr(-1), TRUE,
  159.                       LongInt(0));
  160.  
  161.       { If the window was created OK, figure out how big
  162.         the window should be and then fill in some
  163.         important fields }
  164.     IF myWindow <> NIL THEN
  165.     BEGIN
  166.         { Start by setting the text parameters and computing
  167.           a minimum size for the window }
  168.       SetPort(myWindow);
  169.       TextMode(SrcOr);
  170.       TextFont(Geneva);
  171.       TextSize(9);
  172.       TextFace([]);
  173.       myWindowSize := MinWindowSize;
  174.       
  175.         { Set the window to the minimum size, and show it }
  176.       SizeWindow(myWindow, myWindowSize.h, myWindowSize.v,
  177.                   FALSE);
  178.       ShowWindow(myWindow);
  179.       SelectWindow(myWindow);
  180.  
  181.         { This one shows that a DA owns the window }
  182.       WindowPeek(myWindow)^.WindowKind := dCtl^.dCtlRefNum;
  183.  
  184.         { And this one lets the desk manager know, too }
  185.       dCtl^.dCtlWindow := Ptr(myWindow);
  186.  
  187.         { We'll keep some info around in the storage field }
  188.       dCtl^.dCtlStorage := NewHandle(SizeOf(ChromaStorage));
  189.  
  190.         { Note whether we put up a color window or not }
  191.       ChromaStorageHandle(dCtl^.dCtlStorage)^^.hasColorQD :=
  192.         theEnv.hasColorQD;
  193.       
  194.         { Store the minimum window size, based on the longest
  195.           description name in the string list }
  196.       ChromaStorageHandle(dCtl^.dCtlStorage)^^.minSize :=
  197.         myWindowSize;
  198.       
  199.       IF theEnv.hasColorQD THEN
  200.       BEGIN
  201.           { Go get the main device, then note the window's
  202.             global location }
  203.         WITH ChromaStorageHandle(dCtl^.dCtlStorage)^^ DO
  204.         BEGIN
  205.           theDevice := GetMainDevice;
  206.           windowPlace := WindowPeek(dCtl^.dCtlWindow)^.
  207.                           contRgn^^.rgnBBox;
  208.         END;
  209.         
  210.           { Make a new palette, and "install" it }
  211.         aPalette := NewPalette( 256, NIL, pmExplicit, 0 );
  212.         SetPalette( myWindow, aPalette, TRUE );
  213.       END;
  214.     END;{ if window was created ok... }
  215.   END;{ if window doesn't exist... }
  216.   DRVROpen := NOErr;
  217. END;
  218.  
  219. FUNCTION DRVRClose(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  220. BEGIN
  221.     { If there's a window up, get rid of it,
  222.       and let the desk manager know, too }
  223.   IF dCtl^.dCtlwindow <> NIL THEN
  224.   BEGIN
  225.     DisposeWindow(WindowPtr(dCtl^.dCtlWindow));
  226.     dCtl^.dCtlWindow := NIL;
  227.   END;
  228.  
  229.     { If we created storage, get rid of it }
  230.   IF dCtl^.dCtlStorage <> NIL THEN
  231.     DisposHandle(dCtl^.dCtlStorage);
  232.  
  233.   DRVRClose := NOErr;
  234. END;
  235.  
  236. FUNCTION DRVRControl(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  237.  
  238.   FUNCTION WhereIsTheDevice(whatDevice:GDHandle):INTEGER;
  239.   CONST
  240.     UTableBase = $11c;
  241.   VAR
  242.     devRefNum:Longint;
  243.     unitTablePtr:^Ptr;
  244.     unitTableBase:Ptr;
  245.     theDCEHandle:AuxDCEHandle;
  246.     theDCEHandlePtr:^AuxDCEHandle;
  247.   BEGIN
  248.       { Get the refnum of the gdevice we're interested in }
  249.     devRefNum := whatDevice^^.gdRefNum;
  250.  
  251.       { If it's zero, return an impossible slot number }
  252.     IF devRefNum = 0 THEN
  253.     BEGIN
  254.       WhereIsTheDevice := 42;
  255.       Exit(WhereIsTheDevice);
  256.     END;
  257.     
  258.       { Turn it into a unit number }
  259.     devRefNum := BNOT(devRefNum);
  260.     
  261.       { Grab the unit table address }
  262.     unitTablePtr := Pointer(UTableBase);
  263.     unitTableBase := unitTablePtr^;
  264.     
  265.       { Go get the address of the device control entry
  266.         for this unit }
  267.     theDCEHandlePtr := Pointer(Ord4(unitTableBase)
  268.                         + devRefNum * 4);
  269.     theDCEHandle := theDCEHandlePtr^;
  270.     
  271.       { Return the slot number of this driver }
  272.     WhereIsTheDevice := theDCEHandle^^.dCtlSlot;
  273.   END;{ FUNCTION WhereIsTheDevice }
  274.  
  275.   PROCEDURE CenterInWindow(VAR theRect:Rect);
  276.   VAR
  277.     rectSize:Point;
  278.     chromaSize:Point;
  279.   BEGIN
  280.       { "Home" the rect first }
  281.     OffsetRect(theRect, -theRect.left, -theRect.top);
  282.  
  283.       { Figure size of rect }
  284.     WITH theRect DO
  285.       SetPt(rectSize, right, bottom);
  286.  
  287.       { Figure size of window }
  288.     WITH GrafPtr(dCtl^.dCtlWindow)^.portRect DO
  289.       SetPt(chromaSize, right - left, bottom - top);
  290.  
  291.       { Position the rect, centered in the window }
  292.     OffsetRect(theRect, (chromaSize.h - rectSize.h) DIV 2,
  293.                 (chromaSize.v - rectSize.v) DIV 2);
  294.   END;{ PROCEDURE CenterInWindow }
  295.  
  296.   PROCEDURE DrawWindow;
  297.   
  298.     PROCEDURE DrawNextString(VAR drawWhere:Point;
  299.                               drawWhat:Str255;
  300.                               drawHow:Style);
  301.     VAR
  302.       someFontInfo:FontInfo;
  303.       oldFace:Style;
  304.     BEGIN
  305.         { Save the text style }
  306.       oldFace := GrafPtr(dCtl^.dCtlWindow)^.txFace;
  307.         { Set the desired text face }
  308.       TextFace(drawHow);
  309.       
  310.         { The "cursor" will be centered already, so move
  311.           back half the width of the string and draw it. }
  312.       Move(-(StringWidth(drawWhat) DIV 2), 0);
  313.       DrawString(drawWhat);
  314.       
  315.         { Move the "cursor" up a line, since we're drawing
  316.           from the bottom of the window toward the top. We're
  317.           letting the descenders overhang, since textmode has
  318.           been set to SrcOr and won't wipe out the overlap. }
  319.       GetFontInfo(someFontInfo);
  320.       drawWhere.v := drawWhere.v - someFontInfo.ascent;
  321.         { Re-center the "cursor" for the next time }
  322.       MoveTo( drawWhere.h, drawWhere.v );
  323.       
  324.         { Restore text face }
  325.       TextFace(oldFace);
  326.     END;{ PROCEDURE DrawNextString }
  327.     
  328.     PROCEDURE NumToHexString(aNumber:INTEGER;
  329.                               VAR aString:Str255);
  330.       { Make a 4 digit hexadecimal number from an integer }
  331.     VAR
  332.       unsignedNum:LONGINT;
  333.       i:INTEGER;
  334.       aDigit:INTEGER;
  335.     BEGIN
  336.         { Make an unsigned hex number from the signed integer }
  337.       IF aNumber>0 THEN
  338.         unsignedNum := aNumber
  339.       ELSE
  340.         unsignedNum := aNumber + 65536;
  341.       
  342.         { Make sure the string has 4 digits (spaces)}
  343.       aString := '    ';
  344.       
  345.         { Fill in the digits, from lsd to msd }
  346.       FOR i := 4 DOWNTO 1 DO
  347.       BEGIN
  348.         aDigit := unsignedNum MOD 16;
  349.         IF aDigit<10 THEN
  350.           aString[i] := chr(ord('0')+aDigit)
  351.         ELSE
  352.           aString[i] := chr(ord('A')+aDigit-10);
  353.         unsignedNum := unsignedNum DIV 16;
  354.       END;
  355.     END;{ PROCEDURE NumToHexString }
  356.  
  357.   VAR 
  358.     howManyColors:INTEGER;
  359.     pixPerSide:Point;
  360.     pixSize:Point;
  361.     portSize:Point;
  362.     pixNumber:INTEGER;
  363.     oldIndex:INTEGER;
  364.     i,j:INTEGER;
  365.     slotNumberString:Str255;
  366.     theFontInfo:FontInfo;
  367.     sorryPict:Handle;
  368.     sorryRect:Rect;
  369.     aPalette:PaletteHandle;
  370.     cursorPt:Point;
  371.     theString:Str255;
  372.   BEGIN
  373.     IF dCtl^.dCtlWindow <> NIL THEN
  374.     BEGIN
  375.       WITH ChromaStorageHandle(dCtl^.dCtlStorage)^^ DO
  376.       BEGIN
  377.           { Prepare to draw the contents of the window }
  378.         EraseRect(GrafPtr(dCtl^.dCtlWindow)^.portRect);
  379.   
  380.           { On a color Mac with CLUT device, show the colors,
  381.             on other machines, put up a warning }
  382.         IF hasColorQD AND (theDevice^^.gdType=clutType) THEN
  383.           BEGIN
  384.               { What is the size of our window? }
  385.             WITH GrafPtr(dCtl^.dCtlWindow)^.portRect DO
  386.               SetPt(portSize, right - left, bottom - top);
  387.             
  388.               { We need to know how many colors are in the
  389.                 clut of the device of interest }
  390.             howManyColors := theDevice^^.gdPMap^^.pmTable^^
  391.                               .ctSize + 1;
  392.       
  393.               { Assume a square arrangement of colors,
  394.                 except for 2 color mode. Calculate pixel
  395.                 size based on vertical size of window,
  396.                 assuming that the window will always
  397.                 be wider than it is tall }
  398.             pixPerSide.v := Round(sqrt(howManyColors));
  399.             IF howManyColors = 2 THEN
  400.               pixPerSide.h := 2
  401.             ELSE
  402.               pixPerSide.h := pixPerSide.v;
  403.   
  404.             pixSize.v := portSize.v DIV pixPerSide.v;
  405.             IF howManyColors = 2 THEN
  406.               pixSize.h := pixSize.v DIV 2
  407.             ELSE
  408.               pixSize.h := pixSize.v;
  409.   
  410.             PenSize(pixSize.h, pixSize.v);
  411.             
  412.               { Keep track of the pixel number and use it to 
  413.                 plot the colors in the palette we'll fill
  414.                 from the device's CLUT }
  415.             pixNumber := 0;
  416.             
  417.               { Fill the palette with the colors from the
  418.                 current device, and activate the changes }
  419.             aPalette := GetPalette(WindowPtr(dCtl^.dCtlWindow));
  420.             CTab2Palette(theDevice^^.gdPMap^^.pmTable,aPalette,
  421.                           pmExplicit,0);
  422.             ActivatePalette(WindowPtr(dCtl^.dCtlWindow));
  423.             
  424.               { Plot colored pixels one row at a time,
  425.                 'til they're all used up }
  426.             FOR i := 0 TO(pixPerSide.v - 1) DO
  427.               FOR j := 0 TO(pixPerSide.h - 1) DO
  428.               BEGIN
  429.                   { Set the foreColor, and draw a dot }
  430.                 PmForeColor(pixNumber);  
  431.                 MoveTo(j * pixSize.h, i * pixSize.v);
  432.                 Line(0, 0);
  433.   
  434.                 pixNumber := pixNumber + 1;
  435.               END;{ Plotting loop... }
  436.                   
  437.               { Put up some device info, first figuring some
  438.                 window positions to put the text, starting at
  439.                 the bottom of the window and centered in the
  440.                 blank part of the window }
  441.             SetPt(cursorPt, portSize.v + (portSize.h - 
  442.                     portSize.v) DIV 2, portSize.v - 2 );
  443.             MoveTo( cursorPt.h, cursorPt.v );
  444.             
  445.               { Put up the mode, then its resource string }
  446.             NumToHexString(theDevice^^.gdMode, theString);
  447.             DrawNextString(cursorPt, theString, [bold]);
  448.             GetIndString(theString, RsrcID(dCtl,
  449.                           infoStrings), modeStr);
  450.             DrawNextString(cursorPt, theString, []);
  451.  
  452.               { Put up the flags, then their resource string }
  453.             NumToHexString(theDevice^^.gdFlags, theString);
  454.             DrawNextString(cursorPt, theString, [bold]);
  455.             GetIndString(theString, RsrcID(dCtl,
  456.                           infoStrings), flagsStr);
  457.             DrawNextString(cursorPt, theString, []);
  458.  
  459.               { Put up the refNum, then its resource string }
  460.             NumToHexString(theDevice^^.gdRefNum, theString);
  461.             DrawNextString(cursorPt, theString, [bold]);
  462.             GetIndString(theString, RsrcID(dCtl,
  463.                           infoStrings), refNumStr);
  464.             DrawNextString(cursorPt, theString, []);
  465.   
  466.               { Put up the slot#, then its resource string }
  467.             NumToHexString(WhereIsTheDevice(theDevice) - 8,
  468.                           theString);
  469.             DrawNextString(cursorPt, theString, [bold]);
  470.             GetIndString(theString, RsrcID(dCtl,
  471.                           infoStrings), slotStr);
  472.             DrawNextString(cursorPt, theString, []);
  473.  
  474.           END { if on a mac II... }
  475.         ELSE
  476.           BEGIN
  477.               { For non-color Macs, or non-clut devices,
  478.                 show a picture saying why Chroma isn't
  479.                 showing pretty colors... }
  480.             sorryPict := GetResource('PICT',
  481.                                 RsrcID(dCtl, sorryPicture));
  482.   
  483.             IF sorryPict <> NIL THEN
  484.             BEGIN
  485.                 { Center the picture in the window,
  486.                   and draw it }
  487.               sorryRect := PicHandle(sorryPict)^^.picFrame;
  488.               CenterInWindow(sorryRect);
  489.   
  490.               DrawPicture(PicHandle(sorryPict), sorryRect);
  491.               ReleaseResource(sorryPict);
  492.             END;{ if picHandle is good... }
  493.           END;{ non-color/non-clut condition }
  494.   
  495.           { Put up the version number in the top right corner}
  496.         GetFontInfo(theFontInfo);
  497.         MoveTo(GrafPtr(dCtl^.dCtlWindow)^.portRect.right -
  498.                 StringWidth(versID), theFontInfo.ascent);
  499.         DrawString(versID);
  500.       END; { WITH ChromaStorage... }
  501.     END;{ if window exists... }
  502.   END;{ PROCEDURE DrawWindow }
  503.   
  504.   FUNCTION WhichDevice(aPoint:Point):GDHandle;
  505.   VAR
  506.     aDevice:GDHandle;
  507.     foundOne:Boolean;
  508.   BEGIN
  509.       { Get the first one, and set the initial condition }
  510.     aDevice := GetDeviceList;
  511.     foundOne := FALSE;
  512.  
  513.     WHILE (aDevice <> NIL) AND NOT foundOne DO
  514.       { assuming it has to be in one of the devices... }
  515.     BEGIN
  516.         { Check to see if this is the one... }
  517.       IF PtInRect(aPoint, aDevice^^.gdRect) THEN
  518.       BEGIN
  519.         WhichDevice := aDevice;
  520.         foundOne := TRUE;
  521.       END;
  522.         { Get the next device in the list }
  523.       aDevice := aDevice^^.gdNextGD;
  524.     END;
  525.   END;{ FUNCTION WhichDevice }
  526.   
  527.   FUNCTION EqualRectSize(rect1, rect2:Rect):Boolean;
  528.     { This function is dedicated to Scott T. Boyd %-)}
  529.   BEGIN
  530.     EqualRectSize :=((rect1.right - rect1.left) =
  531.                       (rect2.right - rect2.left)) AND
  532.                       ((rect1.bottom - rect1.top) =
  533.                       (rect2.bottom - rect2.top));
  534.   END;{ FUNCTION EqualRectSize }
  535.   
  536.   PROCEDURE ZoomIt(partCode:INTEGER; clickedWhere:Point;
  537.                     optKeyDown:Boolean);
  538.   CONST
  539.     mBarHeight = $BAA;
  540.   VAR
  541.     oldRect, newRect:Rect;
  542.     maxHeight:INTEGER;
  543.     menuBarHeight:^INTEGER;
  544.     doZoom:Boolean;
  545.   BEGIN
  546.       { Assume we'll zoom }
  547.     doZoom := TRUE;
  548.       { Get the old window size, described by the
  549.         content region }
  550.     oldRect := WindowPeek(dCtl^.dCtlWindow)^.
  551.                 contRgn^^.rgnBBox;
  552.     
  553.       { If the optionKey is down, shrink the window to
  554.         original size... unless, of course, it's already
  555.         original size. }
  556.     IF optKeyDown THEN BEGIN
  557.       newRect := oldRect;
  558.       newRect.left := newRect.right -
  559.                       ChromaStorageHandle(dCtl^.dCtlStorage)^^.
  560.                         minSize.h;
  561.       newRect.bottom := newRect.top +
  562.                       ChromaStorageHandle(dCtl^.dCtlStorage)^^.
  563.                         minSize.v;
  564.  
  565.         { If it's not already small, make it small, otherwise
  566.           DON'T ZOOM THE WINDOW! }
  567.       IF NOT EqualRect(oldRect,newRect) THEN
  568.         WITH WindowPeek(dCtl^.dCtlWindow)^ DO
  569.           wStateHandle(dataHandle)^^.stdState := newRect
  570.       ELSE
  571.         doZoom := FALSE;
  572.     END
  573.     ELSE BEGIN
  574.         { Figure out which device the zoom box was on, so we
  575.           know which monitor to fill up with the window. }
  576.       newRect := WhichDevice(clickedWhere)^^.gdRect;
  577.       InsetRect(newRect, 3, 3);
  578.       newRect.top := newRect.top + 18;{ titlebar }
  579.         { If it's the main device, make room for menu bar }
  580.       IF WhichDevice(clickedWhere)=GetMainDevice THEN
  581.       BEGIN
  582.         menuBarHeight := Pointer( mBarHeight );
  583.         newRect.top := newRect.top + menuBarHeight^;
  584.       END;
  585.   
  586.         { Make sure that the window will be wider than
  587.           it is tall. It should be no taller than its width
  588.           minus the slot info area on the right side.  The
  589.           info area's size can be calculated from the minSize
  590.           width - minSize height. }
  591.       WITH ChromaStorageHandle(dCtl^.dCtlStorage)^^ DO
  592.         maxHeight := newRect.right - newRect.left -
  593.                       (minSize.h - minSize.v);
  594.         { If it's higher than it should be, shorten it }
  595.       IF newRect.bottom - newRect.top > maxHeight THEN
  596.         newRect.bottom := newRect.top + maxHeight;
  597.       
  598.         { If we're not already a big window, zoom to it,
  599.           by putting the new size in stdState.  Otherwise,
  600.           leave the old size there (from last time)}
  601.       IF NOT EqualRect(oldRect, newRect) THEN
  602.         WITH WindowPeek(dCtl^.dCtlWindow)^ DO
  603.           wStateHandle(dataHandle)^^.stdState := newRect;
  604.     END;{ if not optionkey... }
  605.  
  606.     IF doZoom THEN
  607.     BEGIN
  608.       EraseRect(GrafPtr(dCtl^.dCtlWindow)^.portRect);
  609.       ZoomWindow(WindowPtr(dCtl^.dCtlWindow), partCode,
  610.                   false);
  611.       InvalRect(GrafPtr(dCtl^.dCtlWindow)^.portRect);
  612.  
  613.         { Fool ZoomWindow into always zooming "out" by
  614.           changing the "zoomed" window size }
  615.       WITH WindowPeek(dCtl^.dCtlWindow)^ DO
  616.         wStateHandle(dataHandle)^^.stdState := oldRect;
  617.     END;
  618.   END;{ PROCEDURE ZoomIt }
  619.   
  620.   PROCEDURE BragALittle;
  621.   VAR
  622.     aboutPict:Handle;
  623.     aboutRect:Rect;
  624.   BEGIN
  625.     WITH ChromaStorageHandle(dCtl^.dCtlStorage)^^ DO
  626.     BEGIN
  627.       EraseRect(GrafPtr(dCtl^.dCtlWindow)^.portRect);
  628.       
  629.         { If they're not on a color Mac, or there's less
  630.           than 16 colors, put up the monochrome version
  631.           of the brag picture.  BTW, the boolean short-
  632.           circuits on non-color Macs so Chroma won't
  633.           try to access non-existent data structures }
  634.       IF (NOT hasColorQD) |
  635.           (theDevice^^.gdPMap^^.pmTable^^.ctSize + 1 < 16)
  636.       THEN
  637.         aboutPict := GetResource('PICT',
  638.                           RsrcID(dCtl, bwChromaPicture))
  639.       ELSE
  640.         aboutPict := GetResource('PICT',
  641.                           RsrcID(dCtl, colorChromaPicture));
  642.     
  643.       IF aboutPict <> NIL THEN
  644.       BEGIN
  645.           { Center the picture, and draw it }
  646.         aboutRect := PicHandle(aboutPict)^^.picFrame;
  647.         CenterInWindow(aboutRect);
  648.     
  649.         DrawPicture(PicHandle(aboutPict), aboutRect);
  650.         ReleaseResource(aboutPict);
  651.       END;
  652.       
  653.         { Wait until the button isn't down (Hi Roger %-)}
  654.       REPEAT UNTIL NOT StillDown;
  655.       
  656.         { Restore the contents of the window }
  657.       InvalRect(GrafPtr(dCtl^.dCtlWindow)^.portRect);
  658.     END;{ WITH ... }
  659.   END;{ PROCEDURE BragALittle }
  660.  
  661. VAR
  662.   eventAt:  EventPtr;
  663.   oldPort:GrafPtr;
  664.   localPoint:Point;
  665.   aDevice:GDHandle;
  666.   clippyPart:Rect;
  667.   oldClip:RgnHandle;
  668.   thePartCode:INTEGER;
  669.   whichWindow:WindowPtr;
  670.   intersections:INTEGER;
  671.   dumbRect:Rect;
  672.   
  673. BEGIN { of DRVRControl }
  674.   WITH ChromaStorageHandle(dCtl^.dCtlStorage)^^ DO
  675.   BEGIN
  676.     GetPort(oldPort);
  677.     SetPort(GrafPtr(dCtl^.dCtlWindow));
  678.  
  679.     CASE ctlPB^.csCode OF
  680.       accEvent:  
  681.         BEGIN
  682.             { Get the event pointer, through some
  683.               coercive trickery }
  684.           eventAt := trix(ctlPB^.CSParam).theEventPtr;
  685.           WITH eventAt^ do
  686.           BEGIN
  687.             CASE what OF
  688.                 { In case of update event, draw the window }
  689.               updateEvt:
  690.                 BEGIN
  691.                     { Save the current clip region, and
  692.                       restore it after the update }
  693.                   oldClip := NewRgn;
  694.                   GetClip(oldClip);
  695.                   BeginUpdate(WindowPtr(eventAt^.message));
  696.                     { If there's no ColorQD, just draw the
  697.                       window;otherwise cycle through all
  698.                       the devices, clipping the window to
  699.                       each device and drawing }
  700.                   IF NOT hasColorQD THEN
  701.                     DrawWindow
  702.                   ELSE BEGIN
  703.                     aDevice := GetDeviceList;
  704.                     
  705.                       { Go until the end of the device list }
  706.                     WHILE aDevice <> NIL DO
  707.                     BEGIN
  708.                         { Check for intersection of the
  709.                           content region with the device }
  710.                       WITH WindowPeek(dCtl^.dCtlWindow)^ DO
  711.                       IF SectRect(contRgn^^.rgnBBox,
  712.                                     aDevice^^.gdRect,
  713.                                     clippyPart)THEN
  714.                       BEGIN
  715.                           { Translate the global rect
  716.                             to local coordinates }
  717.                         GlobalToLocal(clippyPart.topLeft);
  718.                         GlobalToLocal(clippyPart.botRight);
  719.                         
  720.                           { Set the clip, so other parts of
  721.                             the window don't get blasted.
  722.                             Set theDevice field of the
  723.                             storageHandle to the device of
  724.                             interest, then draw the window. }
  725.                         ClipRect(clippyPart);
  726.                         theDevice := aDevice;
  727.                         DrawWindow;
  728.                       END;{ if intersect... }
  729.                       { Go to the next device in the
  730.                         device list }
  731.                     aDevice := aDevice^^.gdNextGD;
  732.                     END;{ while there's more devices... }
  733.                   END;{ if a color machine... }
  734.  
  735.                   EndUpdate(WindowPtr(eventAt^.message));
  736.                   
  737.                     { Restore the clip, dump the region }
  738.                   SetClip(oldClip);
  739.                   DisposeRgn(oldClip);
  740.  
  741.                     { Mark the window's position }
  742.                   windowPlace := WindowPeek(dCtl^.dCtlWindow)^.
  743.                                   contRgn^^.rgnBBox;
  744.                 END;
  745.               mouseDown:
  746.                 BEGIN
  747.                   localPoint := where;
  748.                   GlobalToLocal(localPoint);
  749.   
  750.                   { Pretend this isn't a DA window to
  751.                     fool FindWindow }
  752.                   WITH WindowPeek(dCtl^.dCtlWindow)^ DO
  753.                     windowKind := -windowKind;
  754.                   thePartCode := FindWindow(where,
  755.                                   WindowPtr(dCtl^.dCtlWindow));
  756.                   WITH WindowPeek(dCtl^.dCtlWindow)^ DO
  757.                     windowKind := -windowKind;
  758.                   
  759.                     { If in zoom box, zoom it, passing along
  760.                       the state of the option key... }
  761.                   IF (thePartCode=inZoomIn) OR
  762.                       (thePartCode=inZoomOut) THEN
  763.                   BEGIN
  764.                     IF TrackBox(WindowPtr(dCtl^.dCtlWindow),
  765.                                 where, thePartCode) THEN
  766.                       ZoomIt(thePartCode, where,
  767.                               BitAnd(modifiers, optionKey)<>0);
  768.                   END ELSE
  769.                       { See if they clicked on the version
  770.                         number.  If so, put up a brag 
  771.                         picture 'til they release the
  772.                         button }
  773.                     WITH localPoint,
  774.                         GrafPtr(dCtl^.dCtlWindow)^.portRect DO
  775.                       IF (h > right - StringWidth(versID))
  776.                           AND (v < 12) THEN
  777.                       BEGIN
  778.                         BragALittle;
  779.                       END;{ if h and v in range... }
  780.                 END;{ mouseDown case }
  781.             END;{ case what ... }
  782.           END;{ with eventAt... }
  783.         END;{ accEvent Case }
  784.  
  785.       accRun:   
  786.         BEGIN      { the periodic call }
  787.           WITH ChromaStorageHandle(dCtl^.dCtlStorage)^^ DO
  788.           BEGIN
  789.               { This is only a problem on multiple device
  790.                 systems }
  791.             IF hasColorQD THEN
  792.             BEGIN
  793.                 { Moved? If yes, invalidate the info part
  794.                   of the window }
  795.               IF NOT EqualRect(WindowPeek(dCtl^.dCtlWindow)^.
  796.                                 contRgn^^.rgnBBox,
  797.                                 windowPlace) THEN
  798.               BEGIN
  799.                 clippyPart := GrafPtr(dCtl^.dCtlWindow)^.
  800.                                       portRect;
  801.                 clippyPart.left := clippyPart.left +
  802.                                     clippyPart.bottom;
  803.                 InvalRect(clippyPart);
  804.               END;{ if window moved... }
  805.             END;{ if has color quickdraw... }
  806.           END;{ with chromastorage... }
  807.         END;{ accRun case... }
  808.     END;{ case ctlPB^.csCode }
  809.     
  810.     SetPort(oldPort);
  811.     DRVRControl := NoErr;
  812.   END;{ with dctl^.dctlstorage... }
  813. END;{ of DRVRControl }
  814.  
  815. FUNCTION DRVRPrime(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  816. BEGIN
  817.   DRVRPrime := NoErr;
  818. END;
  819.  
  820. FUNCTION DRVRStatus(ctlPB:ParmBlkPtr;dCtl:DCtlPtr):OSErr;
  821. BEGIN
  822.   DRVRStatus := NoErr;
  823. END;
  824.  
  825.  
  826. END. {of Chroma UNIT}
  827.  
  828.